package com.wmsafe.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.wmsafe.common.utils.JwtUtil;
import com.wmsafe.common.utils.ResponseData;
import com.wmsafe.gateway.entity.LoginUser;
import com.wmsafe.gateway.utils.RedisCache;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import reactor.core.publisher.Flux;

import java.nio.charset.StandardCharsets;

public class AuthorizationFilter extends AbstractGatewayFilterFactory<Object> {

    @Autowired
    private RedisCache redisCache;

    @Value(value = "${white-list:}")//从nacos中的cloud-gateway-service-6008.yml中拿到白名单列表
    private String whiteListStr;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String pathString = request.getPath().toString();

            if(StringUtils.isNotEmpty(whiteListStr)){
                String[] whitelist = whiteListStr.split(",");
                //使用 AntPathMatcher 类来实现路径的模式匹配 AntPathMatcher 是 Spring 框架中提供的路径匹配工具类，支持常用的 Ant 风格的路径匹配规则
                AntPathMatcher pathMatcher = new AntPathMatcher();

                for (String whiteUrl : whitelist) {
                    //匹配是从下标 0 开始匹配的 第一个参数是白名单 第二个参数是当前请求的路径
                    boolean match = pathMatcher.match(whiteUrl, pathString);
                    if(match){
                        return chain.filter(exchange);//是在白名单，后面放行
                    }
                }
            }

            HttpHeaders headers = request.getHeaders();
            String tokenHeader = headers.getFirst("token");

            try {

                Claims claims = JwtUtil.parseJWT(tokenHeader);
                String userId = claims.getSubject();

                String loginUserJsonData = redisCache.getCacheObject("login:" + userId).toString();
                JSONObject loginUserJsonDataObject = JSONObject.parseObject(loginUserJsonData);
                loginUserJsonDataObject.remove("@type"); //去除鉴权服务序列化后的指定类型 以免影响后面的转换，转换成网关包内的LoginUser类型

                LoginUser loginUser = loginUserJsonDataObject.toJavaObject(LoginUser.class);

                if (loginUser == null || !loginUser.getPermissions().contains("system:dept:index")) {
                    // 设置 HTTP 响应码和响应内容
                    exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                    // 设置 HTTP 响应头 Content-Type 的值为 application/json
                    exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
                    // 定义返回的错误信息
                    ResponseData responseData = new ResponseData(500,null,"权限不足");

                    byte[] bytes = JSONObject.toJSONString(responseData).getBytes(StandardCharsets.UTF_8);
                    DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                    return exchange.getResponse().writeWith(Flux.just(buffer));
                }

            } catch (Exception e) {
                // 设置 HTTP 响应码和响应内容
                exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                // 设置 HTTP 响应头 Content-Type 的值为 application/json
                exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
                // 定义返回的错误信息
                ResponseData responseData = new ResponseData(500,null,"鉴权失败，token解析错误");

                byte[] bytes = JSONObject.toJSONString(responseData).getBytes(StandardCharsets.UTF_8);
                DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                return exchange.getResponse().writeWith(Flux.just(buffer));
//                    return exchange.getResponse().setComplete();
            }

            return chain.filter(exchange);
        };
    }
}
